<?php

require_once('commons_constantes.php');
require_once('commons_utils.php');
require_once('krn_dao.php');


//Définition des constantes d'accesseurs de la table
define('TABLE_DEFINITION'		,0);
define('TABLE_ALL_GET'			,1);
define('TABLE_NAME_GET'			,2);
define('TABLE_DB_GET'			,3);
define('TABLE_COLUMN_GET'		,4);


/**
 * Fonction de définition d'une table de base de données
 */
function tableDefinition
	($getter
	,$codeTable
	,$dbOfTable = null
	,$tableName = null
	,$columns = null
	)
{
	static $tables = null;
	switch ($getter) {
		case TABLE_DEFINITION:
			$tables[$codeTable] = array('bdd'		=> $dbOfTable
									   ,'table'		=> $tableName
									   ,'colonnes'	=> $columns
									   );
			return null;
			break;
		case TABLE_ALL_GET:
			if (isset($tables[$codeTable])) {
				return $tables[$codeTable];
			}
			return null;
			break;
		case TABLE_NAME_GET:
			if (isset($tables[$codeTable])) {
				return $tables[$codeTable]['table'];
			}
			return null;
			break;
		case TABLE_DB_GET:
			if (isset($tables[$codeTable])) {
				return $tables[$codeTable]['bdd'];
			}
			return null;
			break;
		case TABLE_COLUMN_GET:
			if (isset($tables[$codeTable])) {
				return $tables[$codeTable]['colonnes'];
			}
			return null;
			break;
		default:
			throw new Exception('Fonction tableDefinition : accesseur ' . $getter . ' non défini', ERR_TECHNICAL);
	}
}

/**
 * Fonction de définition d'une table
 * @param string $codeTable : le code de la table
 * @param string $dbOfTable : le nom de la base de données à laquelle appartient la table
 * @param string $tableName : le nom de la table telle que connue par la base de données
 * @param string $columns : le tableau des colonnes de la table
 * @return null null est retourné lorsque la table a été initialisée
 */
function tableDefine
	($codeTable
	,$dbOfTable
	,$tableName
	,$columns
	)
{
	return tableDefinition(TABLE_DEFINITION, $codeTable, $dbOfTable, $tableName, $columns);
}

/**
 * Retourne toutes les informations relatives à la table codée par $codeTable
 */
function tableAllGet($codeTable) {
	return tableDefinition(TABLE_ALL_GET, $codeTable);
}

/**
 * Retourne le nom de la table dont est extraite la table codée par $codeTable
 */
function tableNameGet($codeTable) {
	return tableDefinition(TABLE_NAME_GET, $codeTable);
}

/**
 * Retourne le nom de la base de données à laquelle appartient la table codée par $codeTable
 */
function tableDBGet($codeTable) {
	return tableDefinition(TABLE_DB_GET, $codeTable);
}

/**
 * Retourne le tableau des colonnes de la table codée par $codeTable
 */
function tableColumnsGet($codeTable) {
	return tableDefinition(TABLE_COLUMN_GET, $codeTable);
}


/*********************************************************************************************
Fonctions pour interagir avec la table
*********************************************************************************************/

/**
 * Retourne le ou les lignes correspondant à la requête donnée par $select et $cond
 * $select = null -> la valeur par defaut est utilisee : *. Les valeurs de $select sont *, null ou un tableau.
 * Le tableau est de la forme : array('count' => '*'
 * 									 ,'count' => 'col1'
 * 									 ,'max' => 'col2'
 * 								 	 ,'col3'
 * 									 ,...
 * 									 )
 * @param string $codeTable
 * @param mixed $select : null, '*' ou array
 * @param string $cond : la clause where. Commence par 'WHERE ...' ou 'GROUP BY ...'. Commence par un mot clé SQL
 * @param array $bindings : le tableau associatif des paramètres liés de la requête select de leur valeur PHP
 * @return array : le tableau des résultats de la requête. Tableau vide si aucun résultat.
 */
function select
	($codeTable
	,$select = null
	,$cond = null //La requete apres from. Peut etre group by ... ou limit ... ou where ... ou encore where ... group by ... limit ...
	,$bindings = null
	)
{
	$tableName = tableNameGet($codeTable);
	$tableCol = tableColumnsGet($codeTable);

	//On construit la requête

	//Construction du select
	$sel = '';
	if(is_null($select) || (!is_array($select) && trim($select) === '*')) {
		$sel = '*';
	}
	elseif (is_array($select)) {
		$cnt = count($select);
		$i = 1;
		foreach($select as $func => $col) {
			if (trim($col) !== '*' && !in_array($col, $tableCol)) {
				throw new Exception('Fonction select : la colonne ' . $col . ' est inconnue pour la table ' . $tableName, ERR_TECHNICAL);
			}
			if(is_numeric($func)) { //On n'applique pas de fonction dans le select pour la colonne $col
				$sel .= $col;
				if($i !== $cnt) {
					$sel .= ',';
				}
				else {
					$sel .= ' ';
				}
			}
			else {//On applique la fonction $func à la colonne $col
				$sel .= $func . '(' . $col . ')';
				if($i !== $cnt) {
					$sel .= ',';
				}
				else {
					$sel .= ' ';
				}
			}
		}
		$i++;
	}

	//Construction du from
	$from = $tableName;


	//Construction de la requête finale
	$query = 'SELECT ' . $sel . ' FROM ' . $from . ' ' . $cond;

	$dbName = tableDBGet($codeTable);
	$sqlSelect = dbQueryExecution($dbName, $query, $bindings);
	if (is_null($sqlSelect)) {//La requête n'a pas pu s'exécuter'
		//trigger_error();
		throw new Exception('La requête "' . $query . '" n\'a pas pu s\'exécuter sur la base ' . $dbName . '! ', ERR_TECHNICAL);
	}

	$results = dbFetchAll($sqlSelect);
	$theResults = array();
	foreach ($results as $key => $aryVal) {
		//$theResults[$key] = array_map(htmlDecode, $aryVal);
		$aryRes = array();
		foreach ($aryVal as $key2 => $elt) {
			$aryRes[$key2] = htmlDecode($elt);
		}
		$theResults[$key] = $aryRes;
	}
	return $theResults;
}

/**
 * Exécution de la requête de sélection en paramètre
 * @param $codeTable code de la table sur laquelle effectuée la requête
 * @param $selQuery requête
 * @param $bindings bindings de la requête
 * @return array : le tableau des résultats de la requête
 */
function executeSelectQuery($codeTable, $selQuery, $bindings = null) {
	$dbName = tableDBGet($codeTable);
	$sqlSelect = dbQueryExecution($dbName, $selQuery, $bindings);
	if (is_null($sqlSelect)) {//La requête n'a pas pu s'exécuter'
		//trigger_error();
		throw new Exception('La requête "' . $selQuery . '" n\'a pas pu s\'exécuter sur la base ' . $dbName . '! ', ERR_TECHNICAL);
	}

	$results = dbFetchAll($sqlSelect);
	$theResults = array();
	foreach ($results as $key => $aryVal) {
		//$theResults[$key] = array_map(htmlDecode, $aryVal);
		$aryRes = array();
		foreach ($aryVal as $key2 => $elt) {
			$aryRes[$key2] = htmlDecode($elt);
		}
		$theResults[$key] = $aryRes;
	}
	return $theResults;
}

/**
 * Insertion des données contenues dans $colValues pour la table de code $codeTable
 * @param $codeTable code de la table dans laquelle insérer les données
 * @param $colValues valeurs à insérer par colonne
 * @param $bindings valeurs liées paramétrées
 * @return int : l'identifiant de la derenière ligne insérée
 */
function insert
	($codeTable
	,$colValues
	,$bindings
	)
{

	$theCol = '(';
	$theValues = '(';
	$cnt = 1;
	$nbInsert = count($colValues);
	foreach($colValues as $col => $value) {
		$theCol .= $col;
		$theValues .= $value;
		if ($cnt !== $nbInsert) {
			$theCol .= ', ';
			$theValues .= ', ';
		}
		$cnt++;
	}
	$theCol .= ')';
	$theValues .= ')';

	//On construit la requête
	$query = 'INSERT INTO ' . tableNameGet($codeTable) . ' ' . $theCol . ' VALUES ' . $theValues;

	$dbName = tableDBGet($codeTable);
	dbConnexionInit($dbName);
	$dbConnect = dbConnexionGet($dbName);
	$sqlInsert = dbQueryExecution($dbName, $query, $bindings);
	if (is_null($sqlInsert)) {//La requête n'a pas pu s'exécuter'
		//trigger_error();
		throw new Exception('La requête "' . $query . '" n\'a pas pu s\'exécuter sur la base ' . $dbName . '! ', ERR_TECHNICAL);
	}

	return $dbConnect -> lastInsertId();
}

/**
 * Mets à jour la table de code $codeTable pour les colonnes contenues dans les clés du tableau $set avec les valeurs qui leur sont associées pour la condition $cond
 *
 * @param $codeTable code de la table
 * @param $set map clé-valeur contenant les colonnes à mettre à jour et les valeurs correspondantes
 * @param $cond condition de sélection des colonnes à mettre à jour
 * @param $bindings tableau des paramètres des valeurs des colonnes à valuer par bindings
 */
function update
	($codeTable
	,$set
	,$cond
	,$bindings
	)
{
	$tableName = tableNameGet($codeTable);
	$dbName = tableDBGet($codeTable);

	//On vérifie que les colonnes données dans le tableau $set sont des colonnes valides pour la table de code $codeTable
	$columns = tableColumnsGet($codeTable);
	$setQuery = '';
	$cnt = 1;
	$tmp = count($set);
	foreach($set as $col => $bind) {
		if (!in_array($col, $columns, true)) {
			//trigger_error();
			throw new Exception('La colonne ' . $col . 'n\'est pas valide pour la table ' . $tableName, ERR_TECHNICAL);
		}
		$setQuery .= $col . '=' . $bind;
		if($cnt !== $tmp) {
			$setQuery .= ', ';
		}
		else {
			$setQuery .= ' ';
		}
		$cnt++;
	}

	//Construction de la requête
	$query = 'UPDATE ' . $tableName . ' SET ' . $setQuery . ' ' . $cond;

	dbConnexionInit($dbName);
	$dbConnect = dbConnexionGet($dbName);
	$sqlUpdate = dbQueryExecution($dbName, $query, $bindings);
	if (is_null($sqlUpdate)) {//La requête n'a pas pu s'exécuter'
		//trigger_error();
		throw new Exception('La requête "' . $query . '" n\'a pas pu s\'exécuter sur la base ' . $dbName . '! ', ERR_TECHNICAL);
	}

	return true;
}

/**
 * Exécute une requête de suppression de données dans la table de code $codeTable
 * @param $codeTable code de la table
 * @param $attr
 * @param $cond
 */
function delete
	($codeTable
	,$attr=null
	,$cond=null
	)
{
	$tableName = tableNameGet($codeTable);
	$dbName = tableDBGet($codeTable);

	$deleteList = '';
	if (isset($attr) && !is_null($attr)) {
		//On vérifie que les colonnes données dans le tableau $attr sont des colonnes valides pour la table de code $codeTable
		$columns = tableColumnsGet($codeTable);
		$cnt = 1;
		$tmp = count($attr);
		foreach($attr as $col) {
			if (!in_array($col, $columns, true)) {
				//trigger_error();
				throw new Exception('La colonne ' . $col . 'n\'est pas valide pour la table ' . $tableName, ERR_TECHNICAL);
			}
			$deleteList .= $col;
			if($cnt !== $tmp) {
				$deleteList .= ', ';
			}
			else {
				$deleteList .= ' ';
			}
			$cnt++;
		}
	}

	//Construction de la requête
	$query = 'DELETE ' . $deleteList . ' FROM ' . $tableName . ' ' . $cond;

	$sqlDelete = dbQueryExecution($dbName, trim($query));
	if (is_null($sqlDelete)) {//La requête n'a pas pu s'exécuter
		//trigger_error();
		throw new Exception('La requête "' . $query . '" n\'a pas pu s\'exécuter sur la base ' . $dbName . '! ', ERR_TECHNICAL);
	}
	return true;
}